﻿using System;
using System.Collections.ObjectModel;
using System.Data;
using System.IO;
using System.Net;
using BlueDot.Data.Core;
using Microsoft.Synchronization.Data;
using Newtonsoft.Json;
using Syncronization.Common;
using Syncronization.Utils;

namespace Syncronization.REST
{
    public class RestSyncProvider : ServerSyncProvider, IServerSyncProvider
    {
        #region Fields

        private JsonSerializer jsonSerializer = new JsonSerializer
                                                             {NullValueHandling = NullValueHandling.Include};

        private string getChangesWebMethod = "GetChanges";
        private string applyChangesWebMethod = "ApplyChanges";

        /// <summary>
        /// Event thrown before sending request to server
        /// </summary>
        public EventHandler<SyncRequestEventArgs> PreSendRequest;

        #endregion

        #region Properties

        private SyncSchema FullSyncSchema { get; set; }

        #endregion

        #region IServerSyncProvider Members
         
        /// <summary>
        /// Gets or sets the URI.
        /// </summary>
        /// <value>The URI.</value>
        public Uri BaseURI { get; set; }

        /// <summary>
        /// Gets or sets the get changes web method.
        /// </summary>
        /// <value>The get changes web method.</value>
        public string GetChangesWebMethod
        {
            get { return getChangesWebMethod; }
            set { getChangesWebMethod = value; }
        }

        /// <summary>
        /// Gets or sets the apply changes web method.
        /// </summary>
        /// <value>The apply changes web method.</value>
        public string ApplyChangesWebMethod
        {
            get { return applyChangesWebMethod; }
            set { applyChangesWebMethod = value; }
        }


        /// <summary>
        /// Adds the schema.
        /// </summary>
        /// <param name="schemaBuilder">The schema builder.</param>
        public void AddSchema(ISchemaBuilder schemaBuilder)
        {            
            if (FullSyncSchema == null)
                FullSyncSchema = new SyncSchema();

            AddSchema(schemaBuilder, FullSyncSchema);
        }

        /// <summary>
        /// Applies the changes.
        /// </summary>
        /// <param name="groupMetadata">The group metadata.</param>
        /// <param name="dataSet">The data set.</param>
        /// <param name="syncSession">The sync session.</param>
        /// <returns></returns>
        public override SyncContext ApplyChanges(SyncGroupMetadata groupMetadata, DataSet dataSet,
                                                 SyncSession syncSession)
        {
            if (BaseURI == null)
                throw new Exception("Base URI must be set");

            if (string.IsNullOrEmpty(ApplyChangesWebMethod))
                throw new Exception("ApplyChangesWebMethod must be set");

            string url = string.Format(
                BaseURI.AbsolutePath.EndsWith("/")
                    ?
                        "{0}{1}"
                    : "{0}/{1}",
                BaseURI, ApplyChangesWebMethod);

            var timeout = new TimeSpan(0, 0, 60);
            HttpWebRequest request = RestHelpers.CreateHttpRequest(
                url,
                HttpMethods.POST, timeout);

            var syncRequest = mfLYSyncRequest.CreateSyncRequest(groupMetadata, 
                syncSession, dataSet, jsonSerializer);

            if (PreSendRequest != null)
                PreSendRequest(this, new SyncRequestEventArgs { SyncRequest = syncRequest, WebRequest = request });

            RestHelpers.AddToRequest(request, jsonSerializer, syncRequest);

            using (var response = (HttpWebResponse)request.GetResponse())
            {
                using (var reader = new StreamReader(response.GetResponseStream()))
                {
                    //Get the resposne from the stream and deserialse back to mobileResponse
                    using (JsonReader r = new JsonTextReader(reader))
                    {
                        var syncResponse = (mfLYSyncResponse)jsonSerializer.Deserialize(r, typeof(mfLYSyncResponse));
                        return mfLYSyncResponse.GetSyncContext(syncResponse, groupMetadata, jsonSerializer);
                    }
                }
            }
        }

        /// <summary>
        /// Gets the changes.
        /// </summary>
        /// <param name="groupMetadata">The group metadata.</param>
        /// <param name="syncSession">The sync session.</param>
        /// <returns></returns>
        public override SyncContext GetChanges(SyncGroupMetadata groupMetadata, SyncSession syncSession)
        {
            if (BaseURI == null)
                throw new Exception("Base URI must be set");

            if (string.IsNullOrEmpty(GetChangesWebMethod))
                throw new Exception("GetChangesWebMethod must be set");

            string url = string.Format(
                BaseURI.AbsolutePath.EndsWith("/")
                    ?
                        "{0}{1}"
                    : "{0}/{1}",
                BaseURI, GetChangesWebMethod);

            var timeout = new TimeSpan(0, 0, 60);
            HttpWebRequest request = RestHelpers.CreateHttpRequest(
                url,
                HttpMethods.POST, timeout);

            mfLYSyncRequest syncRequest = mfLYSyncRequest.CreateSyncRequest(groupMetadata, syncSession);

            if (PreSendRequest != null)
                PreSendRequest(this, new SyncRequestEventArgs {SyncRequest = syncRequest, WebRequest = request});

            RestHelpers.AddToRequest(request, jsonSerializer, syncRequest);

            using (var response = (HttpWebResponse) request.GetResponse())
            {
                using (var reader = new StreamReader(response.GetResponseStream()))
                {
                    //Get the resposne from the stream and deserialse back to mobileResponse
                    using (JsonReader r = new JsonTextReader(reader))
                    {
                        var syncResponse = (mfLYSyncResponse) jsonSerializer.Deserialize(r, typeof (mfLYSyncResponse));
                        return mfLYSyncResponse.GetSyncContext(syncResponse, groupMetadata, jsonSerializer);
                    }
                }
            }
        }

        /// <summary>
        /// Gets the schema.
        /// </summary>
        /// <param name="tableNames">The table names.</param>
        /// <param name="syncSession">The sync session.</param>
        /// <returns></returns>
        public override SyncSchema GetSchema(Collection<string> tableNames, SyncSession syncSession)
        {
            return FullSyncSchema;
        }
        
        public override void Dispose()
        {
            jsonSerializer = null;
        }

        #endregion

        #region public

        /// <summary>
        /// Adds the schema.
        /// </summary>
        /// <param name="schemaBuilder">The schema builder.</param>
        /// <param name="schema">The schema.</param>
        public void AddSchema(ISchemaBuilder schemaBuilder, SyncSchema schema)
        {
            SyncSchemaUtils.CreateSyncSchemTable(schemaBuilder, schema);
        }

        #endregion
    }
}